<?php defined('SYSPATH') OR die('No direct script access.');

abstract class Model extends Kohana_Model {

	public static $_instances = array();
	public static $_default = '';
	private $_cache = null;

	private $page_with_data = TRUE;


	public function __construct()
	{
		//parent::__construct();
		
		$_cache_config = Kohana::$config->load('cache');
		$this->_cache = Cache::instance($_cache_config['default_driver']);

	}

	public static function instance($type = NULL)
	{
		if ($type === NULL)
		{
			// Use the default type
			$type = Model::$_default;
		}

		if ( ! isset(Model::$_instances[$type]))
		{
			// Set the session class name
			$class = empty($type) ? 'Model' : 'Model_'.ucfirst($type);

			// Create a new session instance
			Model::$_instances[$type] = new $class();

		}

		return Model::$_instances[$type];
	}

	/**
	 * 开关make_page中数据的输出,关闭后,make_page只会返回总数和页码,data将会为空
	 * 比如在后台首页,存在大量的count工作
	 * 这样,可以先关闭读取具体的数据,统计完成后再开启,
	 * 此方法可以优化大量统计中的系统性能,因为程序只做了COUNT工作,而没有查找具体数据
	 * 
	 * @param  bool $_enabled 开关
	 */
	public function enable_page_data($_enabled)
	{
		$this->page_with_data = $_enabled;
	}

	/**
	 * 针对$query的分页函数
	 *
	 * @param  mixed  $query       例如: $query = DB::select()->from('table_name')
	 * @param  string  $selector    参数$query中的select()参数,可以为数组,或逗号(,)分隔的字符串
	 * @param  string  $count_field 取哪个字段做Count(`字段`),比如: id，如果非JOIN、GROUP BY语句，一般为 * 即可
	 * @param  string  $as_array_id 参数$query->execute()->as_array(此处的字段), 默认为NULL,则表示按数字做序号
	 * @param  integer $page        第几页,默认为[1],此参数会根据[总数量]和[pagesize]自动修正
	 * @param  integer $pagesize    单页数量
	 * @return 得到一个格式化的数组,包含: 页码, 总数量, 页数, 单页数量, 和数据
	 */
	protected function make_page(&$query, $selector = '*', $count_field = '*', $as_array_id = NULL, $page= 1, $pagesize = 0)
	{
		$_query = clone $query;
		$count = 0;$data = array();
		if ($pagesize > 0 || !$this->page_with_data) //page_with_data為FALSE時,不返回数据,避免性能浪费
		{
			$_db = Database::$default;

			$t = clone $_query;
			$count = $t->select(array(DB::expr('COUNT('.  $this->_db->quote_column($count_field) .')'),'count'))->reset('order_by')->execute()->get('count');

			if (is_string($selector))
				$_query->select_array( _array_selector_subkey($selector) );
			else
				$_query->select_array( $selector );
			$pagecount = $count > 0 && $pagesize > 0 ? ceil($count / $pagesize) : 1;
			$page > $pagecount && $page = $pagecount;$page < 1 && $page = 1;
			$pagesize > 0 && $_query->limit($pagesize)->offset(($page - 1) * $pagesize);
			$this->page_with_data && $data = $_query->execute()->as_array($as_array_id);
		}
		else
		{
			if (is_string($selector))
				$_query->select_array( _array_selector_subkey($selector) );
			else
				$_query->select_array( $selector );
			$data = $_query->execute()->as_array($as_array_id);
			$count = count($data);
		}
		$pagecount = $count > 0 ? ($pagesize > 0 ? ceil($count / $pagesize) : 1) : 1; //至少是一页
		return compact('page','pagesize','pagecount','count','data');
	}

	/**
	 * 对数组进行分页，此函数主要用于缓存
	 * 如果数组为关系数组,则会保留键名
	 *
	 * @param  array  $_data    原始数组
	 * @param  integer $page    页码,会根据[总数量]和[pagesize]自动修正
	 * @param  integer $pagesize 单页数量
	 * @return [type]           返回一个格式化后的数组,和make_page返回的结构一致
	 */
	protected function make_page_bydata(&$_data, $page = 1, $pagesize = 0)
	{
		$data = $_data;
		$count = count($data);
		$pagecount = $count > 0 ? ($pagesize > 0 ? ceil($count / $pagesize) : 1) : 1;
		$page > $pagecount && $page = $pagecount; $page < 1 && $page = 1;
		//关系数组,则保留键名
		$pagesize > 0 && $data = array_slice($data, ($page - 1) * $pagesize, $pagesize, is_assoc($data) ? TRUE : FALSE);
		return compact('page','pagesize','pagecount','count','data');
	}

	/**
	 * 获取Cache
	 *
	 * @param  string $key    缓存的KEY
	 * @param  mixed $default 如果没有读取到,则默认返回此值
	 * @return mixed          返回数据
	 */
	protected function get_cache($key, $default = NULL)
	{
		return $this->_cache->get($this->cache_name($key), $default);
	}

	/**
	 * 设置任何内容到CACHE
	 * 本函数主要是区别于set_cache
	 *
	 * @param  string $key   缓存的KEY
	 * @param  mixed $data   需要缓存的数据
	 * @param  integer $expire 到期时间，默认1天
	 * @return bool          是否成功设置
	 */
	protected function push_cache($key, $data, $expire = Date::DAY)
	{
		return $this->_cache->set($this->cache_name($key), $data, $expire);
	}

	/**
	 * 设置非FALSE的内容到CACHE
	 * 针对mysql_query,如果查询不到记录集,返回的是FALSE,这里将不纳入缓存范畴
	 * 如果需要缓存FALSE的情况,可以使用put_cache
	 *
	 * @param  string $key   缓存的KEY
	 * @param  mixed $data   需要缓存的数据
	 * @param  integer $expire 到期时间，默认1天
	 * @return bool
	 */
	protected function set_cache($key, $data, $expire = Date::DAY)
	{
		if ($data === FALSE) //数据库返回false则不记录
			return FALSE;
		return $this->_cache->set($this->cache_name($key),$data,$expire);
	}

	/**
	 * 删除缓存
	 * @param  string $key 需要删除的缓存KEY,可以传入多个参数
	 * @return bool
	 */
	protected function delete_cache($key/*, $key2, $key3, ...*/)
	{
		for ($i=0; $i < func_num_args(); $i++) {
			$this->_cache->delete($this->cache_name(func_get_arg($i)));
		}
		return TRUE;
	}

	/**
	 * 删除所有缓存
	 *
	 * @return bool
	 */
	protected function delete_allcache()
	{
		return $this->_cache->delete_all();
	}

	/**
	 * 生成缓存KEY
	 * 注意,默认key的作用域在调取它的class下,比如'Model_Member'类下'u1' 和 'Model_GROUP'类下'u1'无冲突
	 * 如果需要使用其它类的KEY,可以使用数组的形式输入,比如['Model_Memeber','u1'],即可取到Model_Member下的u1
	 * 
	 * @param  mixed $key 
	 * @return [type]      [description]
	 */
	private function cache_name($key)
	{
		if (is_array($key))
			$key = '/'.Cookie::$salt.'/'.implode('/', $key);
		else
			$key = '/'.Cookie::$salt.'/'.get_called_class().'/'.$key;
		return Cookie::$salt.md5($key);
	}

}
